/**
 * @file   AbstractClient.h
 * @author Djuro Drljaca (djurodrljaca@gmail.com)
 * @date   2013-05-18
 * @brief  DataBus Abstract Client.
 *
 * Copyright (C) 2013  Djuro Drljaca <djurodrljaca@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef LIBDATABUS_CLIENT_ABSTRACTCLIENT_H
#define LIBDATABUS_CLIENT_ABSTRACTCLIENT_H

#include <DataBus/Common/Common.h>
#include <DataBus/Common/Packet.h>
#include <DataBus/Common/PacketParser.h>
#include <DataBus/Common/PropertyInfo.h>
#include <DataBus/Common/MethodInfo.h>
#include <DataBus/Common/ValueInfo.h>
#include <DataBus/Common/Value.h>
#include <QtCore/QObject>
#include <QtCore/QString>
#include <QtCore/QList>
#include <QtCore/QTime>
#include <QtCore/QTimer>

namespace DataBus
{
/**
 * @brief The Abstract Client class
 */
class DATABUSSHARED_EXPORT AbstractClient : public QObject
{
    Q_OBJECT

public:
    /**
     * @brief Constructor for AbstractClient
     *
     * @param parent
     */
    explicit AbstractClient(QObject *parent);

    /**
     * @brief Destructor for AbstractClient
     */
    virtual ~AbstractClient();

    /**
     * @brief Returns Client ID
     *
     * @return Client ID
     */
    quint8 getClientId() const;

    /**
     * @brief Returns Client Name
     *
     * @return Client Name
     */
    QString getClientName() const;

    /**
     * @brief Returns Activity Timeout
     *
     * @return Activity Timeout
     */
    quint8 getActivityTimeout() const;

    /**
     * @brief Returns Ping Response Timeout
     *
     * @return Ping Response Timeout
     */
    quint8 getPingResponseTimeout() const;

    /**
     * @brief Returns Ping Retry Count
     *
     * @return Ping Retry Count
     */
    quint8 getPingRetryCount() const;

    /**
     * @brief Checks if client is registered
     *
     * @retval true     Client is registered
     * @retval false    Client is not registered
     */
    bool isRegistered() const;

    /**
     * @brief Sends a GetClientListRequest packet
     *
     * @retval true     Success
     * @retval false    Error
     */
    bool sendGetClientListRequestPacket();

    /**
     * @brief Sends a GetClientNameRequest packet
     *
     * @param clientId  Client ID
     *
     * @retval true     Success
     * @retval false    Error
     */
    bool sendGetClientNameRequestPacket(const quint8 clientId);

    /**
     * @brief Sends a GetClientStatusRequest packet
     *
     * @param clientId  Client ID
     *
     * @retval true     Success
     * @retval false    Error
     */
    bool sendGetClientStatusRequestPacket(const quint8 clientId);

    /**
     * @brief Sends a GetClientPropertyListRequest packet
     *
     * @param clientId  Client ID
     *
     * @retval true     Success
     * @retval false    Error
     */
    bool sendGetClientPropertyListRequestPacket(const quint8 clientId);

    /**
     * @brief Sends a GetClientPropertyInfoRequest packet
     *
     * @param clientId      Client ID
     * @param propertyId    Property ID
     *
     * @retval true     Success
     * @retval false    Error
     */
    bool sendGetClientPropertyInfoRequestPacket(const quint8 clientId, const quint8 propertyId);

    /**
     * @brief Sends a GetClientPropertyValueRequest packet
     *
     * @param clientId      Client ID
     * @param propertyId    Property ID
     *
     * @retval true     Success
     * @retval false    Error
     */
    bool sendGetClientPropertyValueRequestPacket(const quint8 clientId, const quint8 propertyId);

    /**
     * @brief Sends a GetClientPropertyValueResponse packet
     *
     * @param clientId      Client ID
     * @param packetId      Packet ID
     * @param propertyId    Property ID
     * @param value         Property Value
     *
     * @retval true     Success
     * @retval false    Error
     */
    bool sendGetClientPropertyValueResponsePacket(const quint8 clientId,
                                                  const quint8 packetId,
                                                  const quint8 propertyId,
                                                  const Value &value);

    /**
     * @brief Sends a SetClientPropertyValueRequest packet
     *
     * @param clientId      Client ID
     * @param propertyId    Property ID
     * @param value         Property Value
     *
     * @retval true     Success
     * @retval false    Error
     */
    bool sendSetClientPropertyValueRequestPacket(const quint8 clientId,
                                                 const quint8 propertyId,
                                                 const Value &value);

    /**
     * @brief Sends a SetClientPropertyValueResponse packet
     *
     * @param clientId      Client ID
     * @param packetId      Packet ID
     * @param propertyId    Property ID
     * @param returnStatus  Return Status
     *
     * @retval true     Success
     * @retval false    Error
     */
    bool sendSetClientPropertyValueResponsePacket(const quint8 clientId,
                                                  const quint8 packetId,
                                                  const quint8 propertyId,
                                                  const quint8 returnStatus);

    /**
     * @brief Sends a GetClientMethodListRequest packet
     *
     * @param clientId  Client ID
     *
     * @retval true     Success
     * @retval false    Error
     */
    bool sendGetClientMethodListRequestPacket(const quint8 clientId);

    /**
     * @brief Sends a GetClientMethodInfoRequest packet
     *
     * @param clientId      Client ID
     * @param methodId      Method ID
     * @param methodInfo    Method Info
     *
     * @retval true     Success
     * @retval false    Error
     */
    bool sendGetClientMethodInfoRequestPacket(const quint8 clientId, const quint8 methodId);

    /**
     * @brief Sends a GetClientMethodInfoRequest packet
     *
     * @param clientId          Client ID
     * @param methodId          Method ID
     * @param parameterIndex    Parameter Index
     *
     * @retval true     Success
     * @retval false    Error
     */
    bool sendGetClientMethodParameterInfoRequestPacket(const quint8 clientId,
                                                       const quint8 methodId,
                                                       const quint8 parameterIndex);

    /**
     * @brief Sends a GetClientMethodReturnValueInfoRequest packet
     *
     * @param clientId          Client ID
     * @param methodId          Method ID
     * @param returnValueIndex  Return Value Index
     *
     * @retval true     Success
     * @retval false    Error
     */
    bool sendGetClientMethodReturnValueInfoRequestPacket(const quint8 clientId,
                                                         const quint8 methodId,
                                                         const quint8 returnValueIndex);

    // TODO: add other packets

protected:
    /**
     * @brief Starts the Client
     *
     * @param clientId              Client ID
     * @param clientName            Client Name
     * @param activityTimeout       Activity Timeout
     * @param pingResponseTimeout   Ping Response Timeout
     * @param pingRetryCount        Ping Retry Count
     * @param registrationTimeout   Registration Timeout (min 5000 ms)
     *
     * @retval true     Success
     * @retval false    Error
     */
    bool start(const quint8 clientId,
               const QString &clientName,
               const quint8 activityTimeout,
               const quint8 pingResponseTimeout,
               const quint8 pingRetryCount,
               const int registrationTimeout = 10000);

    /**
     * @brief Stops the Client
     */
    void stop();

    /**
     * @brief Generates next Packet ID for this client
     *
     * @return Packet ID
     */
    quint8 getNextPacketId();

    /**
     * @brief Sends a DataBus Packet
     *
     * @param packet    DataBus Packet
     *
     * @retval true     Success
     * @retval false    Error
     */
    virtual bool sendPacket(const Packet &packet) = 0;

    /**
     * @brief Process received data
     *
     * @param receivedData  Received data
     */
    void processReceivedData(const QByteArray &receivedData);

    /**
     * @brief Process received packet
     *
     * @param packet    Received packet
     */
    virtual void processReceivedPacket(const Packet &packet) = 0;

    /**
     * @brief Get Property ID list
     *
     * @return Property ID list
     *
     * Override this method if the client will contain DataBus Properties.
     *
     * Default implementation just returns an empty list.
     */
    virtual QList<quint8> getPropertyIdList() const;

    /**
     * @brief Get Property Info
     *
     * @param propertyId        Property ID
     *
     * @param[out] propertyInfo Property Info
     *
     * @retval true     Success
     * @retval false    Error
     *
     * Override this method if the client will contain DataBus Properties.
     *
     * Default implementation just returns an error.
     */
    virtual bool getPropertyInfo(const quint8 propertyId, PropertyInfo *propertyInfo) const;

    /**
     * @brief Get Property Value
     *
     * @param propertyId            Property ID
     *
     * @param[out] propertyValue    Property Value
     *
     * @retval true     Success
     * @retval false    Error
     *
     * Override this method if the client will contain DataBus Properties.
     *
     * Default implementation just returns an error.
     */
    virtual bool getPropertyValue(const quint8 propertyId, Value *propertyValue);

    /**
     * @brief Set Property Value
     *
     * @param propertyId    Property ID
     * @param propertyValue Property Value
     *
     * @retval true     Success
     * @retval false    Error
     *
     * Override this method if the client will contain DataBus Properties.
     *
     * Default implementation just returns an error.
     */
    virtual bool setPropertyValue(const quint8 propertyId, const Value &propertyValue);

    /**
     * @brief Get Method ID list
     *
     * @return Method ID list
     *
     * Override this method if the client will contain DataBus Methods.
     *
     * Default implementation just returns an empty list.
     */
    virtual QList<quint8> getMethodIdList() const;

    /**
     * @brief Get Method Info
     *
     * @param methodId  Method ID
     *
     * @param[out] methodInfo   Method Info
     *
     * @retval true     Success
     * @retval false    Error
     *
     * Override this method if the client will contain DataBus Methods.
     *
     * Default implementation just returns an error.
     */
    virtual bool getMethodInfo(const quint8 methodId, MethodInfo *methodInfo) const;

    /**
     * @brief Get Method Parameter Info
     *
     * @param methodId          Method ID
     * @param parameterIndex    Parameter Index
     *
     * @param[out] methodInfo   Method Info
     *
     * @retval true     Success
     * @retval false    Error
     *
     * Override this method if the client will contain DataBus Methods.
     *
     * Default implementation just returns an error.
     */
    virtual bool getMethodParameterInfo(const quint8 methodId,
                                        const quint8 parameterIndex,
                                        ValueInfo *parameterInfo) const;

    /**
     * @brief Get Method Return Value Info
     *
     * @param methodId          Method ID
     * @param returnValueIndex  Return Value Index
     *
     * @param[out] returnValueInfo  Return Value Info
     *
     * @retval true     Success
     * @retval false    Error
     *
     * Override this method if the client will contain DataBus Methods.
     *
     * Default implementation just returns an error.
     */
    virtual bool getMethodReturnValueInfo(const quint8 methodId,
                                          const quint8 returnValueIndex,
                                          ValueInfo *returnValueInfo) const;

signals:
    /**
     * @brief Signals if client was registered successfully
     *
     * @param success   Registration status
     */
    void registered(bool success);

    /**
     * @brief Signals if client has stopped
     */
    void stopped();

private slots:
    /**
     * @brief This slot processes clients activity (ping)
     */
    void processActivity();

    /**
     * @brief Process registration timeout
     */
    void processRegistrationTimeout();

private:
    /**
     * @brief Disable AbstractClient copy constructor
     */
    AbstractClient(const AbstractClient &);

    /**
     * @brief Disable AbstractClient operator =
     *
     * @return Nothing
     */
    AbstractClient &operator=(const AbstractClient &);

    /**
     * @brief Processes received packets
     */
    void processReceivedPackets();

    /**
     * @brief Process PingRequest packet
     *
     * @param packet    PingRequest packet
     */
    void processPingRequestPacket(const Packet &packet);

    /**
     * @brief Process RegisterClientResponse packet
     *
     * @param packet    RegisterClientResponse packet
     */
    void processRegisterClientResponsePacket(const Packet &packet);

    /**
     * @brief Process GetClientNameRequest packet
     *
     * @param packet    GetClientNameRequest packet
     */
    void processGetClientNameRequestPacket(const Packet &packet);

    /**
     * @brief Process GetClientPropertyListRequest packet
     *
     * @param packet    GetClientPropertyListRequest packet
     */
    void processGetClientPropertyListRequestPacket(const Packet &packet);

    /**
     * @brief Process GetClientPropertyInfoRequest packet
     *
     * @param packet    GetClientPropertyInfoRequest packet
     */
    void processGetClientPropertyInfoRequestPacket(const Packet &packet);

    /**
     * @brief Process GetClientPropertyValueRequest packet
     *
     * @param packet    GetClientPropertyValueRequest packet
     */
    void processGetClientPropertyValueRequestPacket(const Packet &packet);

    /**
     * @brief Process SetClientPropertyValueRequest packet
     *
     * @param packet    SetClientPropertyValueRequest packet
     */
    void processSetClientPropertyValueRequestPacket(const Packet &packet);

    /**
     * @brief Process GetClientMethodListRequest packet
     *
     * @param packet    GetClientMethodListRequest packet
     */
    void processGetClientMethodListRequestPacket(const Packet &packet);

    /**
     * @brief Process GetClientMethodInfoRequest packet
     *
     * @param packet    GetClientMethodInfoRequest packet
     */
    void processGetClientMethodInfoRequestPacket(const Packet &packet);

    /**
     * @brief Process GetClientMethodParameterInfoRequest packet
     *
     * @param packet    GetClientMethodParameterInfoRequest packet
     */
    void processGetClientMethodParameterInfoRequestPacket(const Packet &packet);

    /**
     * @brief Process GetClientMethodReturnValueInfoRequest packet
     *
     * @param packet    GetClientMethodReturnValueInfoRequest packet
     */
    void processGetClientMethodReturnValueInfoRequestPacket(const Packet &packet);

    /**
     * @brief Client ID
     */
    quint8 m_clientId;

    /**
     * @brief Client Name
     */
    QString m_clientName;

    /**
     * @brief Activity Timeout
     */
    quint8 m_activityTimeout;

    /**
     * @brief Ping Response Timeout
     */
    quint8 m_pingResponseTimeout;

    /**
     * @brief Ping Retry Count
     */
    quint8 m_pingRetryCount;

    /**
     * @brief Packet parser
     */
    PacketParser m_packetParser;

    /**
     * @brief Used for generation of Packet IDs
     */
    quint8 m_packetIdCounter;

    /**
     * @brief Server Timeout
     */
    int m_serverTimeout;

    /**
     * @brief Server Timeout Timer
     */
    QTime m_serverTimeoutTimer;

    /**
     * @brief Timer component
     */
    QTimer m_timer;

    /**
     * @brief Holds the client registation state
     */
    bool m_registered;
};
}

#endif // LIBDATABUS_CLIENT_ABSTRACTCLIENT_H
